#include "HttpRequest.h"
#include <sstream>

using namespace std;
using namespace network;

HttpRequest HttpRequest::parseRequest(const string &request) {
    HttpRequest req;
    req.parse(request);
    return req;
}

void HttpRequest::parse(const string &request) {
    istringstream stream(request);
    string line;
    if (!getline(stream, line)) {
        throw runtime_error("Empty request line");
    }
    parseRequestLine(line);
    while (getline(stream, line) && line != "\r" && !line.empty()) {
        parseHeaderLine(line);
    }
    streampos body_start_pos = stream.tellg();
    size_t remaining_length = request.size() - body_start_pos;
    if (remaining_length > 0) {
        body_ = request.substr(body_start_pos, remaining_length);
    } else {
        body_ = "";
    }
}

string HttpRequest::getUrl() const {
    string::size_type pos = url_.find('?');
    if (pos != string::npos) {
        return {&url_[0], &url_[pos]};
    }
    return url_;
}

unordered_map<string, string> HttpRequest::getUrlParameter() const {
    size_t start = url_.find_first_of('?');
    if (start == string::npos) {
        return unordered_map<string, string>();
    }
    size_t end = 0;
    unordered_map<string, string> ret;
    while (true) {
        end = url_.find_first_of('=', start + 1);
        if (end == string::npos) { break; }
        string key = string(&url_[start + 1], &url_[end]);
        start = end;
        end = url_.find_first_of('&', start + 1);
        if (end == string::npos) {
            string val = string(&url_[start + 1], &url_[url_.size()]);
            ret.emplace(key, val);
            break;
        }
        string val = string(&url_[start + 1], &url_[end]);
        start = end;
        ret.emplace(key, val);
    }
    return ret;
}

void HttpRequest::parseRequestLine(const string &line) {
    istringstream stream(line);
    if (!(stream >> method_ >> url_ >> version_)) {
        throw runtime_error("Failed to parse request line: " + line);
    }
}

void HttpRequest::parseHeaderLine(const string &header) {
    size_t colon_pos = header.find(':');
    if (colon_pos == string::npos) {
        throw runtime_error("Invalid header line: " + header);
    }
    string key = header.substr(0, colon_pos);
    if (header.at(colon_pos + 1) == ' ') {
        ++colon_pos;
    }
    string value = header.substr(colon_pos + 1);
    if (value[value.size() - 1] == '\r') {
        value.pop_back();
    }
    header_.emplace(key, value);
}

string HttpRequest::toString() const {
    ostringstream stream;
    stream << method_ << ' ' << url_ << ' ' << version_ << "\r\n";
    for (const auto &header: header_) {
        stream << header.first << ": " << header.second << "\r\n";
    }
    stream << "\r\n" << body_;
    return stream.str();
}
